#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace AbstractClass.Net
{
    /// <summary>
    /// Makes a HTTP-Post request to a Uniform Resource Identifier (URI).
    /// </summary>
    public class HttpPostRequest
    {
        private readonly string _url;
        private Func<WebResponse> _delegate;
        private WebRequest _request;

        /// <summary>
        /// Initializes a new instance of the <see cref="HttpPostRequest"/> class.
        /// </summary>
        /// <param name="url">The URI that identifies the Internet resource.</param>
        public HttpPostRequest(string url) : this()
        {
            _url = url;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HttpPostRequest"/> class.
        /// </summary>
        /// <param name="request">The <see cref="WebRequest"/> to use for HTTP-Post request.</param>
        public HttpPostRequest(WebRequest request) : this()
        {
            _request = request;
            _url = request.RequestUri.AbsoluteUri;
        }

        private HttpPostRequest()
        {
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
            ContentType = "application/x-www-form-urlencoded";
            TimeOut = 2 * 60 * 1000;
        }

        /// <summary>
        /// Gets the URI that identifies the Internet resource.
        /// </summary>
        /// <value>The URI that identifies the Internet resource.</value>
        public string Url
        {
            get { return _url; }
        }

        /// <summary>
        /// Gets or sets the proxy information for the request.
        /// </summary>
        /// <value>The proxy information for the request.</value>
        public WebProxy Proxy { get; set; }

        /// <summary>
        /// Gets or sets the data for HTTP-Post request.
        /// </summary>
        /// <value>The data for HTTP-Post request.</value>
        public string PostData { get; set; }

        /// <summary>
        /// Gets or sets the <see cref="System.Net.Mime.ContentType"/> of the request.
        /// </summary>
        /// <value>The <see cref="System.Net.Mime.ContentType"/> of the request.</value>
        public string ContentType { get; set; }

        /// <summary>
        /// Gets or sets the time out of the request.
        /// </summary>
        /// <value>The time out of the request.</value>
        public int TimeOut { get; set; }

        /// <summary>
        /// Submits the HTTP-Post request.
        /// </summary>
        /// <returns>A <see cref="WebResponse"/> that contains the response from the Internet resource.</returns>
        public WebResponse Post()
        {
            if (_request == null)
            {
                _request = WebRequest.Create(_url);
                if (Proxy != null)
                    _request.Proxy = Proxy;
            }

            _request.ContentType = ContentType;

            _request.Method = "POST";

            byte[] bytes = Encoding.ASCII.GetBytes(PostData);
            _request.ContentLength = bytes.Length;

            Stream stream = _request.GetRequestStream();
            stream.Write(bytes, 0, bytes.Length);
            stream.Close();
            WebResponse response = _request.GetResponse();
            return response;
        }

        /// <summary>
        /// Submits the HTTP-Post request and gets the response data returned from the Internet resource.
        /// </summary>
        /// <returns>The response data returned from the Internet resource.</returns>
        public string PostAndGetData()
        {
            WebResponse webResponse = Post();
            Stream stream = webResponse.GetResponseStream();
            if (stream != null)
            {
                using (var streamReader = new StreamReader(stream))
                {
                    string response = streamReader.ReadToEnd();
                    return response.Trim();
                }
            }
            return string.Empty;
        }

        /// <summary>
        /// Begins an asynchronous HTTP-Post request.
        /// </summary>
        /// <param name="callback">The <see cref="AsyncCallback"/> delegate.</param>
        /// <param name="state">The state object for this operation.</param>
        /// <returns>An <see cref="IAsyncResult"/> that references the asynchronous request for a response.</returns>
        public IAsyncResult BeginPost(AsyncCallback callback, object state)
        {
            _delegate = Post;
            IAsyncResult asyncResult = _delegate.BeginInvoke(callback, state);
            ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, TimeoutCallback, _request, TimeOut, true);
            return asyncResult;
        }

        private static void TimeoutCallback(object state, bool timedOut)
        {
            if (!timedOut) return;
            var request = state as HttpWebRequest;
            if (request != null)
            {
                request.Abort();
            }
        }

        /// <summary>
        /// Ends an asynchronous HTTP-Post request.
        /// </summary>
        /// <param name="asyncResult">The status of this asynchronous operation.</param>
        /// <returns>A <see cref="WebResponse"/> that contains the response from the Internet resource.</returns>
        public WebResponse EndPost(IAsyncResult asyncResult)
        {
            var postRequest = (HttpPostRequest) asyncResult.AsyncState;
            WebResponse response = postRequest._delegate.EndInvoke(asyncResult);
            return response;
        }
    }
}